package actor
import (
	"github.com/eapache/channels"
	"strings"
)

const (
	STOPPED = iota
	STARTING
	RUNNING
	STOPPING
)

type Context struct {
	system    *ActorSystem
	parent    Actor
	children  map[string]Actor
	state     int
	mailbox   *channels.ResizableChannel
	stop_flag chan bool
}

func (this *Context) start(receiveFunc func(msg interface{}, sender Actor), self Actor) {
	this.state = STARTING
	this.mailbox.Resize(1)
	go func() {
		for {
			select {
			case msg := <-this.mailbox.Out():
				if msg != nil {
					message := msg.(*Message)
					m := message.message

					if event, ok := m.(*event); ok {
						this.handleEvent(event, message.sender, self)
					} else {
						receiveFunc(m, message.sender)
					}
				}
			case <-this.stop_flag:
				return
			}
		}
	}()
	this.state = RUNNING
}

func (this *Context) resize() {
	cap := int(this.mailbox.Cap())
	len := this.mailbox.Len()
	if (len > (cap * 3 / 4)) {
		this.mailbox.Resize(channels.BufferCap(cap * 2))
	}else if (len < int(cap / 32)) {
		this.mailbox.Resize(channels.BufferCap(cap / 2))
	}

}

func (this *Context) actorOf(name string, parent Actor) SimpleActor {
	actor := SimpleActor{
		name:name,
		context:&Context{
			system:this.system,
			parent:parent,
			children:make(map[string]Actor),
			state: STOPPED,
			mailbox:channels.NewResizableChannel(),
			stop_flag: make(chan bool, 1),
		},
	}
	this.children[name] = &actor
	return actor
}

func (this *Context) getActorByName(name string) Actor {
	actor, exists := this.children[name]
	if exists {
		return actor
	} else {
		return nil
	}
}



func (this *Context) ActorSelect(path string) Actor {
	path = strings.TrimSpace(path)

	var children map[string]Actor
	if strings.Index(path, "/") == 0 {
		children = this.system.user.context.children
	}else {
		children = this.children
	}
	names := strings.Split(path, "/")

	var actor Actor

	for _, name := range names {
		name = strings.TrimSpace(name)
		if name != "" || name != "/" {
			ac, exists := children[name]
			if exists {
				actor = ac
				children = ac.Context().children
			}
		}
	}

	return actor
}

func (this *Context) handleEvent(event *event, sender Actor, self Actor) {
	switch event.num {
	case poison_pill:
		this.state = STOPPING
		this.mailbox.Close()
		if len(this.children) > 0 {
			for _, v := range this.children {
				v.Tell(PoisonPill, self)
			}
		} else {
			self.doStop()
			this.parent.Tell(stopped, self)
			this.state = STOPPED
		}
	case terminal:
		delete(this.children, sender.Name())
		self.Tell(&Terminal{}, sender)
		if this.state == STOPPING && len(this.children) == 0 {
			self.doStop()
			this.parent.Tell(&Terminal{}, self)
			this.state = STOPPED
		}
	}
}
